﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD.Core {
    /// <summary>
    /// ボーン変形 マテリアル
    /// </summary>
    /// <remarks>
    /// シェーダーにスキニング用のボーン行列をエクスポートするためのマテリアル クラス。
    /// このマテリアルは <see cref="SkinnedMesh"/> クラスの持つボーン情報からボーン行列をシェーダー側にエクスポートするのに使用します。
    /// シェーダー内部でスキニングを行う場合に、ボーン行列にアクセスするためのサンプルです。
    /// <para>
    /// <h3>スキニング</h3>
    /// 
    /// </para>
    /// </remarks>
    /// <seealso cref="Material"/>
    /// <seealso cref="SkinnedMesh"/>
    public class BoneTransformMaterial : Material {

        #region Field
        SkinnedMesh msh;
        #endregion

        #region Property
        /// <summary>
        /// スキニング行列
        /// </summary>
        /// <remarks>
        /// 頂点座標（ローカル座標）にこの行列を乗算すると変形後の頂点座標（ローカル座標）が求まります。
        /// 座標系は変形前、変形後ともにローカル座標です。
        /// ボーン変形の詳細については、 <see cref="BoneTransformMaterial"/> の解説を参照してください。
        /// 配列のインデックスにはボーン インデックスを使用します。
        /// </remarks>
        /// <exception cref="InvalidOperationException"><see cref="SkinnedMesh"/> が <c>null</c></exception>
        /// <value>スキニング行列の配列</value>
        /// <seealso cref="SkinnedMesh"/>
        public Matrix4x4[] SkinningMatrices {
            get {
                if (msh == null) {
                    throw new InvalidOperationException ("SkinnedMesh is not set");
                }
                var mat = new Matrix4x4[msh.BoneCount];
                for (var i = 0; i < msh.BoneCount; i++) {
                    mat[i] = msh.GetSkinningMatrix (i);
                }
                return mat;
            }
        }

        /// <summary>
        /// 法線用のスキニング行列
        /// </summary>
        /// <remarks>
        /// このプロパティは <see cref="SkinningMatrices"/> プロパティの法線バージョンで、<see cref="Matrix3x3"/> 行列を返します。
        /// 頂点法線にこの行列をにかけると変形後の法線が求まります。
        /// ボーン変形の詳細については、 <see cref="BoneTransformMaterial"/> の解説を参照してください。
        /// 配列のインデックスにはボーン インデックスを使用します。
        /// </remarks>
        /// <exception cref="InvalidOperationException"><see cref="SkinnedMesh"/> が <c>null</c></exception>
        /// <value>法線用のスキニング行列の配列</value>
        public Matrix3x3[] NormalSkinningMatrices {
            get {
                if (msh == null) {
                    throw new InvalidOperationException ("SkinnedMesh is not set");
                }
                var mat = new Matrix3x3[msh.BoneCount];
                for (var i = 0; i < mat.Length; i++) {
                    mat[i] = msh.GetSkinningMatrix3x3 (i);
                }
                return mat;
            }
        }

        /// <summary>
        /// スキンメッシュ コンポーネント
        /// </summary>
        /// <remarks>
        /// このマテリアルが情報を取得する <see cref="SkinnedMesh"/> オブジェクトです。
        /// </remarks>
        /// <value><see cref="SkinnedMesh"/> オブジェクト、または <c>null</c></value>
        public SkinnedMesh SkinnedMesh {
            get { return msh; }
        }

        /// <inheritdoc/>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <remarks>
        /// 初期値は、
        /// <list type="bullet">
        ///   <item><see cref="SkinnedMesh"/> = <c>null</c></item>
        /// </list>
        /// </remarks>
        public BoneTransformMaterial () {
            this.msh = null;
        }
        #endregion

        #region Method
        /// <summary>
        /// スキンメッシュ コンポーネントの変更
        /// </summary>
        /// <remarks>
        /// ボーンの変形の元になる <see cref="SkinnedMesh"/> オブジェクトへの参照を変更します。
        /// このオブジェクトのプロパティは <paramref name="msh"/> オブジェクトから作成されます。
        /// </remarks>
        /// <param name="msh">スキンメッシュ コンポーネント</param>
        public void SetSkinnedMesh (SkinnedMesh msh) {
            this.msh = msh;
        }
        #endregion
    }
}
